# Copyright (c) 2025 Huawei Technologies Co., Ltd.
# openUBMC is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#         http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
import os
import shutil
import subprocess

from conan import ConanFile
from conan.tools.files import apply_conandata_patches, export_conandata_patches, get, copy

required_conan_version = ">=2.13.0"


class QemuDriversConan(ConanFile):
    name = "qemu_drivers"
    description = "some necessary drivers for qemu with virtio-fs support"
    topics = ("conan", "tools", "qemu_drivers", "virtio-fs")
    license = "GPL-2.0-or-later"
    homepage = "https://www.kernel.org/"
    url = "https://github.com/conan-io/conan-center-index"
    settings = "os", "arch", "compiler", "build_type"
    exports_sources = ["permissions.ini", "Makefile", "mock/*"]

    options = {
        "module_symvers": ["ANY"]
    }
    default_options = {
        "module_symvers": "",
    }

    def configure(self):
        pass
    
    def config_options(self):
        module_symvers_path = os.path.join(self.buildenv.vars(self).get("CONAN_CMAKE_FIND_SDK_ROOT"), "Module.symvers")
        self.options.module_symvers = subprocess.check_output(["sha256sum", module_symvers_path], text=True).split()[0]
        print(f"module_symvers_path: {module_symvers_path}, self.options.module_symvers: {self.options.module_symvers}")

    def export_sources(self):
        export_conandata_patches(self)

    def source(self):
        get(self, **self.conan_data["sources"][self.version])
        # 复制 virtio-fs 相关代码（替换原来的9p代码）
        copy(self, "fs/fuse/*", src="linux-5.10.1", dst=".")
        copy(self, "drivers/virtio/*", src="linux-5.10.1", dst=".")
        copy(self, "drivers/block/*", src="linux-5.10.1", dst=".")
        copy(self, "drivers/net/*", src="linux-5.10.1", dst=".")

    def _fix_virtio_fs(self):
        """修复 virtio_fs.c 中的编译错误"""
        virtio_fs_path = os.path.join(self.build_folder, "fs/fuse/virtio_fs.c")
        if not os.path.exists(virtio_fs_path):
            self.output.warning(f"virtio_fs.c not found at {virtio_fs_path}")
            return

        self.output.info("Fixing virtio_fs.c compilation issues...")
        
        with open(virtio_fs_path, 'r') as f:
            content = f.read()

        # 修复 1: 修改 copy_args_to_argbuf 函数
        old_copy_to = """static int copy_args_to_argbuf(struct fuse_req *req)
{
	struct fuse_args *args = req->args;
	unsigned int offset = 0;
	unsigned int num_in;
	unsigned int num_out;
	unsigned int len;
	unsigned int i;

	num_in = args->in_numargs - args->in_pages;
	num_out = args->out_numargs - args->out_pages;
	len = fuse_len_args(num_in, (struct fuse_arg *) args->in_args) +
	      fuse_len_args(num_out, args->out_args);

	req->argbuf = kmalloc(len, GFP_ATOMIC);
	if (!req->argbuf)
		return -ENOMEM;

	for (i = 0; i < num_in; i++) {
		memcpy(req->argbuf + offset,
		       args->in_args[i].value,
		       args->in_args[i].size);
		offset += args->in_args[i].size;
	}

	return 0;
}"""

        new_copy_to = """static int copy_args_to_argbuf(struct fuse_req *req)
{
	/* In Linux 5.10.1, fuse_req doesn't have argbuf member */
	/* We'll handle args directly in sg_init_fuse_args */
	return 0;
}"""

        # 修复 2: 修改 copy_args_from_argbuf 函数  
        old_copy_from = """static void copy_args_from_argbuf(struct fuse_args *args, struct fuse_req *req)
{
	unsigned int remaining;
	unsigned int offset;
	unsigned int num_in;
	unsigned int num_out;
	unsigned int i;

	remaining = req->out.h.len - sizeof(req->out.h);
	num_in = args->in_numargs - args->in_pages;
	num_out = args->out_numargs - args->out_pages;
	offset = fuse_len_args(num_in, (struct fuse_arg *)args->in_args);

	for (i = 0; i < num_out; i++) {
		unsigned int argsize = args->out_args[i].size;

		if (args->out_argvar &&
		    i == args->out_numargs - 1 &&
		    argsize > remaining) {
			argsize = remaining;
		}

		memcpy(args->out_args[i].value, req->argbuf + offset, argsize);
		offset += argsize;

		if (i != args->out_numargs - 1)
			remaining -= argsize;
	}

	/* Store the actual size of the variable-length arg */
	if (args->out_argvar)
		args->out_args[args->out_numargs - 1].size = remaining;

	kfree(req->argbuf);
	req->argbuf = NULL;
}"""

        new_copy_from = """static void copy_args_from_argbuf(struct fuse_args *args, struct fuse_req *req)
{
	/* In Linux 5.10.1, fuse_req doesn't have argbuf member */
}"""

        # 应用修复
        content = content.replace(old_copy_to, new_copy_to)
        content = content.replace(old_copy_from, new_copy_from)
        
        # 修复 3: 移除对 copy_args_from_argbuf 的调用
        content = content.replace("\tcopy_args_from_argbuf(args, req);", "\t/* copy_args_from_argbuf(args, req); */")
        
        # 修复 4: 修改 sg_init_fuse_args 调用
        content = content.replace("req->argbuf, &argbuf_used", "NULL, NULL")
        content = content.replace("req->argbuf + argbuf_used, NULL", "NULL, NULL")
        
        # 修复 5: 移除 argbuf 相关的清理代码
        content = content.replace("""\tif (ret < 0 && req->argbuf) {
\t\tkfree(req->argbuf);
\t\treq->argbuf = NULL;
\t}""", "")

        with open(virtio_fs_path, 'w') as f:
            f.write(content)
        
        self.output.info("Successfully fixed virtio_fs.c")

    def build(self):
        apply_conandata_patches(self)
        if self.settings.arch == "x86_64":
            return
        
        # 修复 virtio_fs 编译问题
        self._fix_virtio_fs()
        
        CONAN_CMAKE_FIND_SDK_ROOT = self.buildenv.vars(self).get("CONAN_CMAKE_FIND_SDK_ROOT")
        make_command = f"make KERNEL_PATH={self.buildenv.vars(self).get('KERNEL_PATH')} KBUILD_EXTRA_SYMBOLS={CONAN_CMAKE_FIND_SDK_ROOT}/Module.symvers CROSS_COMPILE={self.buildenv.vars(self).get('CHOST')}-"
        self.run(f"{make_command}")

    def package(self):
        if self.settings.arch == "x86_64":
            return
        copy(self,
            "**/*.ko", src=".", dst=os.path.join(self.package_folder, 'lib/modules/qemu/'), keep_path=False)
        copy(self, "permissions.ini", src=".", dst=self.package_folder)